﻿using System;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using System.Threading.Tasks;

namespace net.xBei.Helper;
/// <summary>
/// JSON 快速操作
/// </summary>
public static class JsonHelper {
    private static JsonSerializerOptions? _options;
    private static JsonSerializerOptions _indentedOptions = new(JsonSerializerOptions.Default) { WriteIndented = true };

    /// <summary>
    /// 使用<see cref="JsonSerializerOptions.Web"/>设置JSON系列化配置，会影响<see cref="JsonHelper"/>的所有方法。
    /// 转义字符串时使用的编码器使用<see cref="System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping"/>
    /// </summary>
    /// <param name="config">额外进行设置</param>
    public static void SetupDefaultOptions(Action<JsonSerializerOptions>? config = default) {
        var web = new JsonSerializerOptions(JsonSerializerOptions.Web) {
            Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
        };
        config?.Invoke(web);
        _options = web;
        _indentedOptions = new JsonSerializerOptions(_options) {
            WriteIndented = true,
        };
    }
    /// <summary>
    /// 尝试 反系列化JSON字串，如果输入的字符串无法正确反系列化则返回 default
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="content"></param>
    /// <returns></returns>
    public static T? TryDeserialize<T>(this string? content)
        => TryDeserialize<T>(content, out var value) ? value : value;

    /// <summary>
    /// 尝试 反系列化JSON字串，如果输入的字符串无法正确反系列化则返回 dv
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="content"></param>
    /// <param name="dv"></param>
    /// <returns></returns>
    public static T TryDeserialize<T>(this string? content, T dv)
        => TryDeserialize<T>(content, out var value) ? (value ?? dv) : dv;
    /// <summary>
    /// 尝试 反系列化JSON字串，
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="content"></param>
    /// <param name="value">如果输入的字符串无法正确反系列化则返回 default</param>
    /// <returns></returns>
    public static bool TryDeserialize<T>(this string? content, [NotNullWhen(true)] out T? value) {
        value = default;
        if (string.IsNullOrWhiteSpace(content)) {
            return false;
        }
        try {
            value = JsonSerializer.Deserialize<T>(content, _options);
        } catch (Exception ex) {
            Console.Error.WriteLine(ex);
            Console.Error.WriteLine(content);
            return false;
        }
        return value != null;
    }
    /// <summary>
    /// 尝试 反系列化JSON字串，如果输入的字符串无法正确反系列化则返回 dv
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="content"></param>
    /// <param name="dv"></param>
    /// <returns></returns>
    public static async Task<T> TryDeserializeAsync<T>(this Task<string> content, T dv) {
        var (success, obj) = await TryDeserializeAsync<T>(content);
        return (success && obj != null) ? obj : dv;
    }

    /// <summary>
    /// 尝试 反系列化JSON字串，如果输入的字符串无法正确反系列化则返回 <see langword="default"/>
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="content"></param>
    /// <returns></returns>
    public static async Task<(bool success, T? obj)> TryDeserializeAsync<T>(this Task<string> content) {
        T? value = default;
        var json = await content.ConfigureAwait(false);
        if (string.IsNullOrWhiteSpace(json)) {
            return (false, value);
        }
        try {
            value = JsonSerializer.Deserialize<T>(json, _options);
            return (value != null, value);
        } catch {
            return (false, value);
        }
    }


    /// <summary>
    /// 系列化数据对象为JSON字串
    /// </summary>
    /// <param name="data"></param>
    /// <param name="indented"></param>
    /// <returns></returns>
    public static string SerializeObject(this object data, bool indented = false)
        => JsonSerializer.Serialize(data, indented ? _indentedOptions : _options);
    /// <summary>
    /// 系列化数据对象为JSON字串
    /// </summary>
    /// <param name="data"></param>
    /// <param name="indented"></param>
    /// <returns></returns>
    public static string ToJson(this object data, bool indented = false) {
        if (data is JsonDocument doc) {
            return doc.RootElement.GetRawText();
        }
        return JsonSerializer.Serialize(data, indented ? _indentedOptions : _options);
    }
    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="Tin"></typeparam>
    /// <param name="data"></param>
    /// <param name="indented"></param>
    /// <returns></returns>
    public static async Task<string> ToJsonAsync<Tin>(this Task<Tin> data, bool indented = false)
        => JsonSerializer.Serialize(await data, indented ? _indentedOptions : _options);
}
